package tpcomp2010.estructura;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

/**
 *  Clase representa una lista de Nodos.
 */

public class ListaNodos extends ArrayList<Nodo> {

    //Id de la lista de Nodos
    private int id;
    private boolean marcado;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    /* Metodo que inserta un nodo a la lista de Nodos.
     * @param n el Nodo a insertar
     *
     */
    public void insertar(Nodo n) {
        this.add(n);
    }

    /* Metodo que obtiene un nodo de la lista por el indice.
     * @param index el indice de la lista de nodos.
     *
     */
    public Nodo getNodo(int index) {
        return this.get(index);
    }

    public Nodo getNodoById(int index) {
        for (Nodo e : this) {
            if (e.getId() == index) {
                return e;
            }
        }
        throw new IndexOutOfBoundsException(" No existe en esta lista un Nodo con id = " + index);
    }

    public int cantidad() {
        return this.size();
    }

    public void resetVisitas() {
        for (int i = 0; i < cantidad(); i++) {
            getNodo(i).setVisitado(false);
        }
    }

    public boolean contiene(Nodo e) {
        if (this.contains(e)) {
            return true;
        }
        return false;
    }
    
    /** Obtiene el nodo inicial de la lista de nodos.
     * @return nodo el nodo inicial.
     *
     */
    public Nodo getNodoInicial() throws Exception {
        int indiceNodoInicial = 0;
        int cantNodosIniciales = 0;
        for (int i = 0; i < cantidad(); i++) {
            if (getNodo(i).isNodoinicial()) {
                indiceNodoInicial = i;
                cantNodosIniciales++;
            }
        }
        if (cantNodosIniciales == 1) {
            return getNodo(indiceNodoInicial);
        } else {
            throw new Exception("Solo puede haber un nodo inicial");
        }
    }

    /** Obtiene la lista de nodos finales.
     * @return nuevaLista lista de nodos finales.
     *
     */
    public ListaNodos getNodoFinales() throws Exception {
        ListaNodos nuevaLista = new ListaNodos();
        for (int i = 0; i < cantidad(); i++) {
            if (getNodo(i).isNodofinal()) {
                nuevaLista.insertar(getNodo(i));
            }
        }
        return nuevaLista;
    }

    /** Verifica si la lista de Nodos actual posee un nodo inicial.
     * @return ini el nodo inicial, null en caso de que no exista.
     */
    public boolean contieneInicial() {
        Nodo ini = null;
        try {
            ini = getNodoInicial();
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    /** Verifica si la lista de Nodos actual posee nodos finales.
     * @return fin lista de nodos finales..
     */
    public boolean contieneFinal() {
        ListaNodos fin;
        try {
            fin = getNodoFinales();
        } catch (Exception ex) {
            return false;
        }

        if (fin.cantidad() > 0) {
            return true;
        } else {
            return false;
        }
    }

    
    private void ordenar() {
        Nodo a[] = new Nodo[1];
        Comparator<Nodo> c = null;

        a = this.toArray(a);

        Arrays.sort(a, c);

        this.removeAll(this);
        for (int i = 0; i < a.length; i++) {
            this.add(a[i]);
        }
    }

    /** Compara dos listas de Nodos.
     * @return result 0 si son iguales, -1 en caso contrario .
     */
    public int compareTo(Object o) {
        int result = -1;
        ListaNodos otro = (ListaNodos) o;
        otro.ordenar();
        this.ordenar();
        if (this.cantidad() == otro.cantidad()) {
            for (int i = 0; i < this.cantidad(); i++) {

                Nodo a = this.getNodo(i);
                try {
                    otro.getNodoById(a.getId());
                } catch (Exception ex) {
                    return -1;
                }
            }
            result = 0; 
        }
        return result;
    }

    /** Verifica si dos listas son iguales.
     * @return true si son iguales .
     * @return false en caso contrario
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof ListaNodos) {
            if (compareTo(o) == 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + this.id;
        hash = 97 * hash + (this.marcado ? 1 : 0);
        return hash;
    }

    /** Retorna un String con la lista de nodos y el id de cada lista.
     * @return result cadena con la lista de nodos.
     */
    public String imprimir() {
        String result = " ";
        result = result + this.getId() + " = { ";
        for (int i = 0; i < this.cantidad(); i++) {
            result = result + (this.get(i)).getId();
            if (!(i == (this.cantidad() - 1))) {
                result = result + ", ";
            }
        }
        result = result + " } ";
        return result;
    }

    /** Retorna un String con la lista de nodos.
     * @return result cadena con la lista de nodos.
     */
    public String imprimirPath() {
        String result = " ";
        result = result + "{ ";
        for (int i = 0; i < this.cantidad(); i++) {
            result = result + (this.get(i)).getId();
            if (!(i == (this.cantidad() - 1))) {
                result = result + ", ";
            }
        }
        result = result + " } ";
        return result;
    }
    

    public boolean isMarcado() {
        return this.marcado;
    }

    public void setMarcado(boolean marcado) {
        this.marcado = marcado;
    }
}
